package pl.allegro.tech.hermes.consumers.consumer.sender;

import static java.util.stream.Collectors.joining;

import com.google.common.collect.ImmutableList;
import java.net.URI;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MultiMessageSendingResult implements MessageSendingResult {

  private final List<SingleMessageSendingResult> children;

  public MultiMessageSendingResult(List<SingleMessageSendingResult> children) {
    this.children = ImmutableList.copyOf(children);
  }

  @Override
  public int getStatusCode() {
    if (succeeded()) {
      return children.stream().mapToInt(MessageSendingResult::getStatusCode).max().orElse(0);
    } else {
      return children.stream()
          .filter(child -> !child.succeeded())
          .mapToInt(MessageSendingResult::getStatusCode)
          .max()
          .orElse(0);
    }
  }

  @Override
  public boolean isLoggable() {
    return children.stream().anyMatch(MessageSendingResult::isLoggable);
  }

  @Override
  public boolean ignoreInRateCalculation(
      boolean retryClientErrors, boolean isOAuthSecuredSubscription) {
    return children.stream()
        .allMatch(r -> r.ignoreInRateCalculation(retryClientErrors, isOAuthSecuredSubscription));
  }

  @Override
  public Optional<Long> getRetryAfterMillis() {
    return children.stream()
        .map(MessageSendingResult::getRetryAfterMillis)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .min(Comparator.naturalOrder());
  }

  @Override
  public boolean succeeded() {
    return !children.isEmpty() && children.stream().allMatch(MessageSendingResult::succeeded);
  }

  @Override
  public boolean isClientError() {
    List<SingleMessageSendingResult> failed =
        children.stream().filter(child -> !child.succeeded()).collect(Collectors.toList());
    return !failed.isEmpty() && failed.stream().allMatch(MessageSendingResult::isClientError);
  }

  @Override
  public boolean isTimeout() {
    return !children.isEmpty() && children.stream().anyMatch(MessageSendingResult::isTimeout);
  }

  @Override
  public boolean isRetryLater() {
    return children.isEmpty() || children.stream().anyMatch(MessageSendingResult::isRetryLater);
  }

  @Override
  public List<MessageSendingResultLogInfo> getLogInfo() {
    return children.stream()
        .map(
            child ->
                new MessageSendingResultLogInfo(
                    child.getRequestUri(), child.getFailure(), child.getRootCause()))
        .collect(Collectors.toList());
  }

  @Override
  public List<URI> getSucceededUris(Predicate<MessageSendingResult> filter) {
    return children.stream()
        .filter(filter)
        .map(SingleMessageSendingResult::getRequestUri)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());
  }

  public List<SingleMessageSendingResult> getChildren() {
    return children;
  }

  @Override
  public String getRootCause() {
    if (children.isEmpty()) {
      return "Empty children message results";
    } else {
      return children.stream()
          .map(
              child ->
                  child.getRequestUri().map(Object::toString).orElse("")
                      + ":"
                      + child.getRootCause())
          .collect(joining(";"));
    }
  }
}
